package com.yygo.dao.hibernate;

import com.yygo.dao.LoanPersonDao;
import com.yygo.model.Address;
import com.yygo.model.LoanPerson;
import com.yygo.model.LoanRequest;
import com.yygo.model.enums.IDType;
import com.yygo.util.DateUtil;
import com.yygo.util.StringUtil;
import org.hibernate.Criteria;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Property;
import org.hibernate.criterion.Restrictions;
import org.springframework.stereotype.Repository;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;

/**
 * Created by tiansha on 2015/7/8.
 */
@Repository("loanPersonDao")
public class LoanPersonDaoHibernate extends GenericDaoHibernate<LoanPerson, Long> implements LoanPersonDao {

    public LoanPersonDaoHibernate() {
        super(LoanPerson.class);
    }

    @Override
    public  Set<Long> findByIdentity(LoanRequest loanRequest, int days) {
        String idNoValue = "";
        if (loanRequest.getRequestPerson().getIdNo().length() > 6)
            idNoValue = loanRequest.getRequestPerson().getIdNo().substring(0, 6);
        else
            idNoValue = loanRequest.getRequestPerson().getIdNo();

        Criteria criteria = getSession().createCriteria(LoanPerson.class).add(Restrictions.not(Restrictions.eq("reqId", loanRequest.getId())))
                .add(Restrictions.eq("idType", IDType.Identity)).add(Restrictions.like("idNo", idNoValue, MatchMode.START));
        Property requestDate = Property.forName("requestDate");
        Date before = DateUtil.getDateBefore(days);
        criteria.add(requestDate.ge(before));

        List<LoanPerson> list = criteria.list();

        Set<Long> requests = new HashSet<>();
        for (LoanPerson person : list) {
            requests.add(person.getReqId());
        }

        return requests;
    }

    @Override
    public Set<Long> findByIdNo(LoanRequest loanRequest) {
        List<String> values = new ArrayList<>();

        String value = loanRequest.getRequestPerson().getIdNo();
        if (!StringUtil.isBlankOrNull(value))
            values.add(value);

        if (loanRequest.getCoPerson() != null) {
            value = loanRequest.getCoPerson().getIdNo();
            if (!StringUtil.isBlankOrNull(value))
                values.add(value);
        }
        if (loanRequest.getDbPerson() != null) {
            value = loanRequest.getDbPerson().getIdNo();
            if (!StringUtil.isBlankOrNull(value))
                values.add(value);
        }

        Property idNo = Property.forName("idNo");
        Criteria criteria = getSession().createCriteria(LoanPerson.class).add(Restrictions.not(Restrictions.eq("reqId", loanRequest.getId()))).add(idNo.in(values));

        List<LoanPerson> list = criteria.list();

        Set<Long> requests = new HashSet<>();
        for (LoanPerson person : list) {
            requests.add(person.getReqId());
        }

        return requests;
    }

    @Override
    public Set<Long> findByUrgentNo(LoanRequest loanRequest, int days) {
        List<String> urgentNos = new ArrayList<>();

        String value = loanRequest.getUrgentContactTelNo1();
        if (!StringUtil.isBlankOrNull(value))
            urgentNos.add(value);

        value = loanRequest.getUrgentContactTelNo2();
        if (!StringUtil.isBlankOrNull(value))
            urgentNos.add(value);
        value = loanRequest.getUrgentContactTelNo3();
        if (!StringUtil.isBlankOrNull(value))
            urgentNos.add(value);

        Criteria criteria = getSession().createCriteria(LoanRequest.class).add(Restrictions.ne("id", loanRequest.getId()));
        // urgent no are equal
        criteria.add(Restrictions.or(Restrictions.in("urgentContactTelNo1", urgentNos), Restrictions.or(Restrictions.in("urgentContactTelNo2", urgentNos), Restrictions.in("urgentContactTelNo3", urgentNos))));

        Property requestDate = Property.forName("requestDate");
        Date before = DateUtil.getDateBefore(days);
        criteria.add(requestDate.ge(before));
        // request person's idNo is different
        criteria.createAlias("requestPerson", "requestPerson").add(Restrictions.ne("requestPerson.idNo", loanRequest.getRequestPerson().getIdNo()));
        List<LoanRequest> list = criteria.list();

        Set<Long> requests = new HashSet<>();
        for (LoanRequest person : list) {
            requests.add(person.getId());
        }

        return requests;
    }

    @Override
    public Set<Long> findByProperty(LoanRequest loanRequest, String property, int days) {
        Set<Long> requests = new HashSet<>();
        Class clazz = LoanPerson.class;
        try {
            LoanPerson person;

            Method method = clazz.getDeclaredMethod("get" + (property.substring(0, 1).toUpperCase()) + property.substring(1));
            person = loanRequest.getRequestPerson();

            findByPropertyInternal(loanRequest.getId(), method, property, person, days, requests);

            if (loanRequest.getCoPerson() != null) {
                findByPropertyInternal(loanRequest.getId(), method, property, loanRequest.getCoPerson(), days, requests);
            }
            if (loanRequest.getDbPerson() != null) {
                findByPropertyInternal(loanRequest.getId(), method, property, loanRequest.getDbPerson(), days, requests);
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }

        return requests;
    }

    private void findByPropertyInternal(Long reqId, Method method, String property, LoanPerson loanPerson, int days, Set<Long> requests) throws Exception {
        String value = "", idNo = "";
        Object object = method.invoke(loanPerson);
        if (object != null)
            value = (String)object;
        if (StringUtil.isBlankOrNull(value)) {
            return;
        }

        idNo = loanPerson.getIdNo();

        Criteria criteria = getSession().createCriteria(LoanPerson.class).add(Restrictions.not(Restrictions.eq("reqId", reqId)))
                .add(Restrictions.eq(property, value)).add(Restrictions.not(Restrictions.eq("idNo", idNo)));
        Property requestDate = Property.forName("requestDate");
        Date before = DateUtil.getDateBefore(days);
        criteria.add(requestDate.ge(before));

        List<LoanPerson> list = criteria.list();

        for (LoanPerson person : list) {
            requests.add(person.getReqId());
        }
    }

    @Override
    public Set<Long> findByAddress(LoanRequest loanRequest, int days) {
        Set<Long> requests = new HashSet<>();
        LoanPerson person = loanRequest.getRequestPerson();
        String addressProperty = "address";
        findByAddressInternal(loanRequest.getId(), person, addressProperty, days, requests);

        if (loanRequest.getCoPerson() != null) {
            findByAddressInternal(loanRequest.getId(), loanRequest.getCoPerson(), addressProperty, days, requests);
        }
        if (loanRequest.getDbPerson() != null) {
            findByAddressInternal(loanRequest.getId(), loanRequest.getDbPerson(), addressProperty, days, requests);
        }
        return requests;
    }

    @Override
    public Set<Long> findByCompanyAddress(LoanRequest loanRequest, int days) {
        Set<Long> requests = new HashSet<>();
        LoanPerson person = loanRequest.getRequestPerson();
        String addressProperty = "companyAddress";
        findByAddressInternal(loanRequest.getId(), person, addressProperty, days, requests);

        if (loanRequest.getCoPerson() != null) {
            findByAddressInternal(loanRequest.getId(), loanRequest.getCoPerson(), addressProperty, days, requests);
        }
        if (loanRequest.getDbPerson() != null) {
            findByAddressInternal(loanRequest.getId(), loanRequest.getDbPerson(), addressProperty, days, requests);
        }
        return requests;
    }

    private void findByAddressInternal(Long reqId, LoanPerson loanPerson, String addressProperty, int days, Set<Long> requests) {
        String value = "", idNo = "";
        Address address = loanPerson.getAddress();
        idNo = loanPerson.getIdNo();

        Criteria criteria = getSession().createCriteria(LoanPerson.class).add(Restrictions.ne("reqId", reqId))
                .add(Restrictions.not(Restrictions.eq("idNo", idNo)))
                .createAlias(addressProperty, "address").add(Restrictions.eq("address.province", address.getProvince())).add(Restrictions.eq("address.city", address.getCity()))
                .add(Restrictions.eq("address.county", address.getCounty())).add(Restrictions.eq("address.address", address.getAddress()));
        Property requestDate = Property.forName("requestDate");
        Date before = DateUtil.getDateBefore(days);
        criteria.add(requestDate.ge(before));

        List<LoanPerson> list = criteria.list();

        for (LoanPerson person : list) {
            requests.add(person.getReqId());
        }
    }
}
